/**
 *
 */
package com.browseengine.bobo.facets.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.log4j.Logger;

import com.browseengine.bobo.api.BrowseFacet;
import com.browseengine.bobo.api.FacetIterator;
import com.browseengine.bobo.api.FacetSpec;
import com.browseengine.bobo.facets.FacetCountCollector;
import com.browseengine.bobo.facets.data.FacetDataCache;
import com.browseengine.bobo.facets.data.TermStringList;
import com.browseengine.bobo.facets.filter.FacetRangeFilter;
import com.browseengine.bobo.facets.filter.GeoSimpleFacetFilter;
import com.browseengine.bobo.util.BigSegmentedArray;
import com.browseengine.bobo.util.LazyBigIntArray;

public class GeoSimpleFacetCountCollector implements FacetCountCollector {

  private static final Logger log = Logger.getLogger(GeoSimpleFacetCountCollector.class.getName());
  private final FacetSpec _spec;
  private final String _name;
  private int[] _latCount;
  private int[] _longCount;
  private final BigSegmentedArray _latOrderArray;
  private final FacetDataCache<?> _latDataCache;
  private final TermStringList _predefinedRanges;
  private int[][] _latPredefinedRangeIndexes;
  private final BigSegmentedArray _longOrderArray;
  private final FacetDataCache<?> _longDataCache;
  private int[][] _longPredefinedRangeIndexes;

  protected GeoSimpleFacetCountCollector(String name, FacetDataCache<?> latDataCache,
      FacetDataCache<?> longDataCache, int docBase, FacetSpec spec, List<String> predefinedRanges) {
    _name = name;
    _latDataCache = latDataCache;
    _longDataCache = longDataCache;
    _latCount = new int[_latDataCache.freqs.length];
    _longCount = new int[_longDataCache.freqs.length];
    log.info("latCount: " + _latDataCache.freqs.length + " longCount: "
        + _longDataCache.freqs.length);
    _latOrderArray = _latDataCache.orderArray;
    _longOrderArray = _longDataCache.orderArray;
    _spec = spec;
    _predefinedRanges = new TermStringList();
    Collections.sort(predefinedRanges);
    _predefinedRanges.addAll(predefinedRanges);

    if (predefinedRanges != null) {
      _latPredefinedRangeIndexes = new int[_predefinedRanges.size()][2];
      _longPredefinedRangeIndexes = new int[_predefinedRanges.size()][2];
      int i = 0;
      for (String range : _predefinedRanges) {
        int[] ranges = GeoSimpleFacetFilter.parse(_latDataCache, _longDataCache, range);
        _latPredefinedRangeIndexes[i][0] = ranges[0]; // latStart
        _latPredefinedRangeIndexes[i][1] = ranges[1]; // latEnd
        _longPredefinedRangeIndexes[i][0] = ranges[2]; // longStart
        _longPredefinedRangeIndexes[i][1] = ranges[3]; // longEnd
        i++;
      }
    }
  }

  /*
   * (non-Javadoc)
   * @see com.browseengine.bobo.facets.FacetCountCollector#collect(int)
   */
  @Override
  public void collect(int docid) {
    // increment the count only if both latitude and longitude ranges are true for a particular
    // docid
    for (int[] range : _latPredefinedRangeIndexes) {
      int latValue = _latOrderArray.get(docid);
      int longValue = _longOrderArray.get(docid);
      int latStart = range[0];
      int latEnd = range[1];
      if (latValue >= latStart && latValue <= latEnd) {
        for (int[] longRange : _longPredefinedRangeIndexes) {
          int longStart = longRange[0];
          int longEnd = longRange[1];
          if (longValue >= longStart && longValue <= longEnd) {
            _latCount[_latOrderArray.get(docid)]++;
            _longCount[_longOrderArray.get(docid)]++;
          }
        }
      }
    }
  }

  /*
   * (non-Javadoc)
   * @see com.browseengine.bobo.facets.FacetCountCollector#collectAll()
   */
  @Override
  public void collectAll() {
    _latCount = _latDataCache.freqs;
    _longCount = _longDataCache.freqs;
  }

  /*
   * (non-Javadoc)
   * @see com.browseengine.bobo.facets.FacetCountCollector#getCountDistribution()
   */
  @Override
  public BigSegmentedArray getCountDistribution() {
    BigSegmentedArray dist = null;
    if (_latPredefinedRangeIndexes != null) {
      dist = new LazyBigIntArray(_latPredefinedRangeIndexes.length);
      int n = 0;
      int start;
      int end;
      for (int[] range : _latPredefinedRangeIndexes) {
        start = range[0];
        end = range[1];
        int sum = 0;
        for (int i = start; i < end; i++) {
          sum += _latCount[i];
        }
        dist.add(n++, sum);
      }
    }
    return dist;
  }

  /*
   * (non-Javadoc)
   * @see com.browseengine.bobo.facets.FacetCountCollector#getName()
   */
  @Override
  public String getName() {
    return _name;
  }

  /*
   * (non-Javadoc)
   * @see com.browseengine.bobo.api.FacetAccessible#getFacet(java.lang.String)
   */
  @Override
  public BrowseFacet getFacet(String value) {
    BrowseFacet facet = null;
    int[] range = FacetRangeFilter.parse(_latDataCache, value);

    if (range != null) {
      int sum = 0;
      for (int i = range[0]; i <= range[1]; ++i) {
        sum += _latCount[i];
      }
      facet = new BrowseFacet(value, sum);
    }
    return facet;
  }

  @Override
  public int getFacetHitsCount(Object value) {
    int[] range = FacetRangeFilter.parse(_latDataCache, (String) value);

    if (range != null) {
      int sum = 0;
      for (int i = range[0]; i <= range[1]; ++i) {
        sum += _latCount[i];
      }
      return sum;
    }
    return 0;
  }

  /*
   * (non-Javadoc)
   * @see com.browseengine.bobo.api.FacetAccessible#getFacets()
   */
  @Override
  public List<BrowseFacet> getFacets() {
    if (_spec != null) {
      if (_latPredefinedRangeIndexes != null) {
        int minCount = _spec.getMinHitCount();
        int[] rangeCounts = new int[_latPredefinedRangeIndexes.length];
        for (int i = 0; i < _latCount.length; ++i) {
          if (_latCount[i] > 0) {
            for (int k = 0; k < _latPredefinedRangeIndexes.length; ++k) {
              if (i >= _latPredefinedRangeIndexes[k][0] && i <= _latPredefinedRangeIndexes[k][1]) {
                rangeCounts[k] += _latCount[i];
              }
            }
          }
        }
        List<BrowseFacet> list = new ArrayList<BrowseFacet>(rangeCounts.length);
        for (int i = 0; i < rangeCounts.length; ++i) {
          if (rangeCounts[i] >= minCount) {
            BrowseFacet choice = new BrowseFacet();
            choice.setFacetValueHitCount(rangeCounts[i]);
            choice.setValue(_predefinedRanges.get(i));
            list.add(choice);
          }
        }
        return list;
      } else {
        return FacetCountCollector.EMPTY_FACET_LIST;
      }
    } else {
      return FacetCountCollector.EMPTY_FACET_LIST;
    }
  }

  @Override
  public void close() {
    // TODO Auto-generated method stub
  }

  @Override
  public FacetIterator iterator() {
    // each range is of the form <lat, lon, radius>
    LazyBigIntArray rangeCounts = new LazyBigIntArray(_latPredefinedRangeIndexes.length);
    for (int i = 0; i < _latCount.length; ++i) {
      if (_latCount[i] > 0) {
        for (int k = 0; k < _latPredefinedRangeIndexes.length; ++k) {
          if (i >= _latPredefinedRangeIndexes[k][0] && i <= _latPredefinedRangeIndexes[k][1]) {
            rangeCounts.add(k, rangeCounts.get(k) + _latCount[i]);
          }
        }
      }
    }
    return new DefaultFacetIterator(_predefinedRanges, rangeCounts, rangeCounts.size(), true);
  }
}
